#include <QtGui>
#include <QtOpenGL/qgl.h>

#include <GL/glut.h>

#include "glchildcsg.h"
#include "../csg_y.tab.c"
#include "../gl2ps.c"

int yywrap()
{
    return 1;
}

extern CSGGrapher * grapher;

GLChildCSG::GLChildCSG()
    : GLChild()
{
    setAttribute(Qt::WA_DeleteOnClose);

    grapherChild = NULL;

    drawFlag = DRAW_GRIDS | DRAW_NAIVELY | DRAW_CTRL_POLYGON;
}

GLChildCSG::~GLChildCSG()
{
    if (grapherChild != NULL)
    {
        delete grapherChild;
        grapherChild = NULL;
    }
}

bool GLChildCSG::loadFile(const QString &fileName)
{
    QFile file(fileName);

    QApplication::setOverrideCursor(Qt::WaitCursor);

    const char* str = fileName.toLatin1().data();

    grapher = new CSGGrapher;

    yyin= fopen( str, "r");

    if (yyin <= 0) {
        QMessageBox::warning(this, tr("GCS"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(fileName)
                             .arg(file.errorString()));

        QApplication::restoreOverrideCursor();
        return false;
    }

    yyparse();

    fclose(yyin);

    grapherChild = grapher;
    grapher = NULL;
    grapherChild->initial();

    if (grapherChild->getGrid())
    {
        drawFlag |= DRAW_GRIDS;
    }
    else
    {
        drawFlag &= ~DRAW_GRIDS;
    }

    testedBox = StarTestBox(grapherChild->x0, grapherChild->x1, grapherChild->y0, grapherChild->y1,
                            grapherChild->z0, grapherChild->z1);

    QApplication::restoreOverrideCursor();

    setCurrentFile(fileName);

    return true;
}

void GLChildCSG::showBoundingBox()
{
    drawFlag ^= DRAW_BOUNDING_BOX;
    repaint();
}

void GLChildCSG::showCtrlPoly()
{
    drawFlag ^= DRAW_CTRL_POLYGON;
    repaint();
}

void GLChildCSG::showContour()
{
    drawFlag ^= DRAW_CONTOUR;
    repaint();
}

void GLChildCSG::showNaive()
{
    drawFlag ^= DRAW_NAIVELY;
    repaint();
}

void GLChildCSG::showCasteljau()
{
    drawFlag ^= DRAW_CASTELJAU;
    repaint();
}

void GLChildCSG::testFinished()
{
    repaint();
}

void GLChildCSG::paintGL()
{
    if (grapherChild == NULL)
    {
        return;
    }

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPointSize(2.0);
    gl2psPointSize(2.0);

    if (!lighting)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        gl2psEnable(GL2PS_BLEND);
        gl2psBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        gl2psDisable(GL_LIGHTING);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_LIGHTING);
    }
    else
    {
        glDisable(GL_BLEND);
        gl2psDisable(GL2PS_BLEND);
        glEnable(GL_DEPTH_TEST);

        float xc = (grapherChild->x0 + grapherChild->x1)/2;
        float yc = (grapherChild->y0 + grapherChild->y1)/2;
        float zc = (grapherChild->z0 + grapherChild->z1)/2;
        float r = sqrt(pow(grapherChild->x1 - grapherChild->x0, 2) + pow(grapherChild->y1 - grapherChild->y0, 2));

        float g_lightPos[4] = {xc + r, yc + r, zc + 2 * r, 1.0f};
        glShadeModel(GL_SMOOTH);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);
        glEnable(GL_NORMALIZE);
        glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    }

    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();

    ViewAll();

    // setup viewpoint from current arcball
    arcball.glDraw();

    if (drawFlag & DRAW_NAIVELY)
    {
        grapherChild->drawNaively();
        if (grapherChild->dim == 3)
        {

        }
    }

    if (drawFlag & DRAW_CONTOUR)
    {
        grapherChild->drawContour();
    }

    if (drawFlag & DRAW_CASTELJAU)
    {
        grapherChild->drawCasteljau();
    }

    glDisable(GL_LIGHTING);
    glColor3f(1.0, 0.0, 0.0);

    if (drawFlag & DRAW_CTRL_POLYGON)
    {
        grapherChild->drawCtrlPolygon();
    }

    if (drawFlag & DRAW_BOUNDING_BOX)
    {
        grapherChild->drawBoundingBox();
    }

    drawSelectingBox();

//    glPopMatrix();

//    glPushMatrix();

    glEnable(GL_LIGHTING);

    CVector3d pos = camera.GetPosition();
    double h = camera.GetFarDistance() * tan(camera.GetHeightAngle()*PI/360.0);
    double w = viewport.GetAspectRatio() * h;
    glTranslatef(pos.x(), pos.y(), 0);
    double domainScale = std::min(w/3, h/3);
    glScalef(domainScale, domainScale, domainScale);

    glEnable(GL_LIGHTING);


    glPopMatrix();

    glFlush();
    glFinish();
}

void GLChildCSG::resetView()
{
    if (grapherChild)
    {
        arcball.Clear();

        arcball.SetCenter((grapherChild->x0 + grapherChild->x1)/2,
                          (grapherChild->y0 + grapherChild->y1)/2,
                          (grapherChild->z0 + grapherChild->z1)/2);

        ViewAll(false);

        repaint();
    }
}

void GLChildCSG::drawSelectingBox()
{
    glEnable(GL_LIGHTING);

//    glBegin(GL_POLYGON);
//    glVertex3d(testedBox.x0, testedBox.y0, testedBox.z0);
//    glVertex3d(testedBox.x0, testedBox.y1, testedBox.z0);
//    glVertex3d(testedBox.x1, testedBox.y1, testedBox.z0);
//    glVertex3d(testedBox.x1, testedBox.y0, testedBox.z0);
//    glEnd();

    glLineWidth(3.0);
    glDisable(GL_LIGHTING);
    glColor3f(1.0, 0.0, 0.0);

    glBegin(GL_LINE_LOOP);
    glVertex3d(testedBox.x0, testedBox.y0, testedBox.z0);
    glVertex3d(testedBox.x0, testedBox.y1, testedBox.z0);
    glVertex3d(testedBox.x1, testedBox.y1, testedBox.z0);
    glVertex3d(testedBox.x1, testedBox.y0, testedBox.z0);
    glEnd();

    glBegin(GL_LINE_LOOP);
    glVertex3d(testedBox.x0, testedBox.y0, testedBox.z1);
    glVertex3d(testedBox.x0, testedBox.y1, testedBox.z1);
    glVertex3d(testedBox.x1, testedBox.y1, testedBox.z1);
    glVertex3d(testedBox.x1, testedBox.y0, testedBox.z1);
    glEnd();

    glBegin(GL_LINES);
    glVertex3d(testedBox.x0, testedBox.y0, testedBox.z0);
    glVertex3d(testedBox.x0, testedBox.y0, testedBox.z1);

    glVertex3d(testedBox.x0, testedBox.y1, testedBox.z0);
    glVertex3d(testedBox.x0, testedBox.y1, testedBox.z1);

    glVertex3d(testedBox.x1, testedBox.y1, testedBox.z0);
    glVertex3d(testedBox.x1, testedBox.y1, testedBox.z1);

    glVertex3d(testedBox.x1, testedBox.y0, testedBox.z0);
    glVertex3d(testedBox.x1, testedBox.y0, testedBox.z1);
    glEnd();
}

void GLChildCSG::ViewAll(bool check_first)
{
    // make it only once
    if(!m_FirstView && check_first)
        return;

    m_FirstView = false;

    // set up the camera to visualize the whole object
//	TRACE("setup camera.");

    CMatrix44 ArcballMatrix = arcball.GetMatrix();

    CVector3d minBound, maxBound;
    minBound.Set(grapherChild->x0,
        grapherChild->y0,
        grapherChild->z0);
    maxBound.Set(grapherChild->x1,
        grapherChild->y1,
        grapherChild->z1);
    minBound = ArcballMatrix * minBound;
    maxBound = ArcballMatrix * maxBound;
    camera.ViewAll(minBound[0],
        maxBound[0],
        minBound[1],
        maxBound[1],
        minBound[2],
        maxBound[2],
        viewport);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    camera.glDraw(viewport);
    glMatrixMode(GL_MODELVIEW);
}
